package com.beerandjesus.sportofkings.handicapper.profile.postprocessor;

import com.beerandjesus.sportofkings.handicapper.model.CramerClassDropJockeySwitchProfile;
import com.beerandjesus.sportofkings.handicapper.model.HorseAnalysisResult;
import com.beerandjesus.sportofkings.handicapper.profile.Profile;
import com.beerandjesus.sportofkings.handicapper.profile.ProfileType;
import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.springframework.stereotype.Component;

import java.util.*;

import static org.apache.commons.collections.CollectionUtils.*;

@SuppressWarnings("unchecked")
@Component
public class CramerClassDropJockeySwitchTiesPostProcessor implements ProfilesPostProcessor {
    public void postProcessProfiles(List<HorseAnalysisResult> results) {
        final Map<HorseAnalysisResult, CramerClassDropJockeySwitchProfile> resultsWithJockeySwitchProfile =
                new HashMap<HorseAnalysisResult, CramerClassDropJockeySwitchProfile>();
        forAllDo(results, toResultToProfileMap(resultsWithJockeySwitchProfile));
        if (resultsWithJockeySwitchProfile.size() > 1) {
            Collection<Map.Entry<HorseAnalysisResult, CramerClassDropJockeySwitchProfile>> nonQualifiers
                    = select(resultsWithJockeySwitchProfile.entrySet(), findTieNonQualifiers());
            int qualifierCount = resultsWithJockeySwitchProfile.size() - nonQualifiers.size();
            switch (qualifierCount) {
                case 1:
                case 2:
                    // Broken tie with 2 or fewer qualifiers. Remove profile non-qualifiers.
                    removeCramerProfileFromNonQualifiers(results, nonQualifiers);
                    break;
                default:
                    // No qualifiers or no broken tie for 3 or more qualifiers. Remove profile for all results.
                    removeCramerProfileFromAllResults(results);
            }
        }
        // One or fewer results with the Cramer profile. No action required.
    }

    private void removeCramerProfileFromNonQualifiers(List<HorseAnalysisResult> results, Collection<Map.Entry<HorseAnalysisResult, CramerClassDropJockeySwitchProfile>> nonQualifiers) {
        List<HorseAnalysisResult> nonQualifyingResults = new ArrayList<HorseAnalysisResult>();
        collect(nonQualifiers, fromMapEntryToMapEntryKey(), nonQualifyingResults);
        for (HorseAnalysisResult result : results) {
            if (!nonQualifyingResults.contains(result)) continue;
            List<Profile> profiles = result.getProfiles();
            if (profiles == null) continue;
            Profile profile = (Profile) find(profiles, cramerClassDropFinder());
            if (profile != null) {
                profiles.remove(profile);
            }
        }
    }

    private static Transformer fromMapEntryToMapEntryKey() {
        return new Transformer() {
            public Object transform(Object o) {
                return ((Map.Entry<HorseAnalysisResult, CramerClassDropJockeySwitchTiesPostProcessor>) o).getKey();
            }
        };
    }

    private void removeCramerProfileFromAllResults(List<HorseAnalysisResult> results) {
        for (HorseAnalysisResult result : results) {
            List<Profile> profiles = result.getProfiles();
            if (profiles == null) continue;
            Profile profile = (Profile) find(profiles, cramerClassDropFinder());
            if (profile != null) {
                profiles.remove(profile);
            }
        }

    }

    private static Predicate findTieNonQualifiers() {
        return new Predicate() {
            public boolean evaluate(Object o) {
                Map.Entry<HorseAnalysisResult, CramerClassDropJockeySwitchProfile> e =
                        (Map.Entry<HorseAnalysisResult, CramerClassDropJockeySwitchProfile>) o;
                return !e.getValue().qualifiesInCaseOfTie();
            }
        };
    }

    private static Closure toResultToProfileMap(final Map<HorseAnalysisResult, CramerClassDropJockeySwitchProfile> map) {
        return new Closure() {
            public void execute(Object o) {
                HorseAnalysisResult analysisResult = (HorseAnalysisResult) o;
                List<Profile> profiles = analysisResult.getProfiles();
                if (profiles != null && profiles.size() > 0) {
                    CramerClassDropJockeySwitchProfile profile = (CramerClassDropJockeySwitchProfile)
                            find(profiles, cramerClassDropFinder());
                    if (profile != null) {
                        map.put(analysisResult, profile);
                    }
                }
            }
        };
    }

    private static Predicate cramerClassDropFinder() {
        return new Predicate() {
            public boolean evaluate(Object o) {
                Profile p = (Profile) o;
                String name = p.getName();
                return name.equals(ProfileType.CRAMER_DROP_IN_CLASS_WITH_JOCKEY_SWITCH.getName()) ||
                        name.equals(ProfileType.CRAMER_DROP_IN_CLASS_POSS_JOCKEY_SWITCH.getName());
            }
        };
    }
}
